//
//  EzDigest.cpp
//  CloudCameraHub
//
//  Created by 刘跃龙 on 11/2/18.
//  Copyright © 2018 Illidan. All rights reserved.
//

#include <stdio.h>
#include "EzDigest.h"

#include "ABMUtils.h"
#include "EzText.h"
#include "EzBase64.h"
#include "MD5.h"
#include <map>

EzDigest::EzDigest()
:m_realm("ezhomelabs")
,m_privateKey(EzText::genCharacterAndNumberPassword(16))
,m_eTag("etag")
,m_timeout(60)//60s timeout
{
    m_userList.clear();
}

EzDigest::~EzDigest()
{
}

void EzDigest::addUserInfo(UserInfo info)
{
    delUserInfo(info);
    m_userList.push_back(info);
}
void EzDigest::delUserInfo(UserInfo info)
{
    for (std::vector<UserInfo>::iterator it = m_userList.begin(); it != m_userList.end(); it++) {
        if (it->username == info.username) {
            m_userList.erase(it);
            return;
        }
    }
}

EzDigest::UserInfo EzDigest::getUserInfo(std::string username)
{
    EzDigest::UserInfo ret;
    ret.username.clear();
    ret.password.clear();
    for (int i = 0 ; i < m_userList.size() ; i ++) {
        if(username == m_userList.at(i).username) return m_userList.at(i);
    }
    return ret;
}

bool EzDigest::checkNonce(std::string nonce)
{
    std::string nonceContent = Ez::base64Decode(nonce);
    size_t pos = nonceContent.find(" ");
    if(pos == std::string::npos)
        return false;
    
    std::string str_timestamp = nonceContent.substr(0,pos);
    long timestamp = atol(str_timestamp.c_str());
    //printf("%ld-%ld>%d => ",Ez::getTimestmap(),timestamp,getTimeout());
    if(Ez::getTimestmap()-timestamp>getTimeout())
    {
        printf("true\n");
        return false;
    }else{
        printf("false\n");
    }
    
    std::string timestamp_md5 = nonceContent.substr(pos+1);
    printf("nonce[%s] ==> [%s]\n",nonce.c_str(),timestamp_md5.c_str());
    
    std::string check_timestamp_md5 = md5(str_timestamp+":"+getETag()+":"+getPrivateKey());
    printf("MD5(%s:%s:%s)=%s\n",str_timestamp.c_str(),getETag().c_str(),getPrivateKey().c_str(),check_timestamp_md5.c_str());
    
    if(check_timestamp_md5 == timestamp_md5)
    {
        return true;
    }else{
        return false;
    }
};

bool EzDigest::checkAuthenticate(
                                 std::string username,
                                 std::string password,
                                 std::string method,
                                 std::string uri,
                                 std::string body,
                                 std::string realm,
                                 std::string nonce,
                                 std::string nc,
                                 std::string algorithm,
                                 std::string cnonce,
                                 std::string qop,
                                 std::string response
                                 )
{
    std::string reqDigest = EzDigest::genDigest(username, password, method, uri, body, realm, nonce, nc, algorithm, cnonce, qop);
    if(response == reqDigest){
        return true;
    }else{
        return false;
    }
}

std::string EzDigest::genDigestContent(std::map<std::string,std::string> map,std::string head)
{
    std::string ret;
    for (std::map<std::string,std::string>::iterator it = map.begin() ; it != map.end(); it++) {
        if (!ret.empty()) ret = ret + ", ";
        ret = ret + it->first + "=\"" + it->second + "\"";
    }
    return head+ret;
}

std::map<std::string,std::string> EzDigest::parseDigestContent(std::string content)
{
    std::map<std::string,std::string> map;
    if(content.substr(0,7) == "Digest "){
        content = content.substr(7);
        std::vector<std::string> list = ABMUtils::split(content, ", ");
        for (int i = 0 ; i < list.size() ; i++ ) {
            size_t pos = list.at(i).find("=");
            if(pos != std::string::npos){
                std::string key = list.at(i).substr(0,pos);
                std::string value = EzText::strRemove(list.at(i).substr(pos+1),'"');
                //printf("[%s]:%s\n",key.c_str(),value.c_str());
                map[key] = value;
            }
        }
    }
    return map;
}

std::string EzDigest::genDigest(
                                std::string username,
                                std::string password,
                                std::string method,
                                std::string uri,
                                std::string body,
                                std::string realm,
                                std::string nonce,
                                std::string nc,
                                std::string algorithm,
                                std::string cnonce,
                                std::string qop
                                )
{
    std::string HA1,HD,HA2;
    if (algorithm == "MD5-sess") {
        HA1 = md5(md5(username+":"+realm+":"+password)+":"+nonce+":"+cnonce);
    }else{
        HA1 = md5(username+":"+realm+":"+password);
    }
    if(qop.empty())
    {
        HD = nonce;
    }else{
        HD = nonce+":"+nc+":"+cnonce+":"+qop;
    }
    if (qop == "auth-int") {
        HA2 = md5(method+":"+uri+":"+md5(body));
    }else{
        HA2 = md5(method+":"+uri);
    }
    return md5(HA1+":"+HD+":"+HA2);
}

std::string EzDigest::genNonce(std::string eTag,std::string privateKey)
{
    std::string ret;
    long timstamp = Ez::getTimestmap();
    ret = Ez::base64Encode(ABMUtils::ultoa(timstamp)+" "+md5(ABMUtils::ultoa(timstamp)+":"+eTag+":"+privateKey)) ;
    return ret;
}

std::string EzDigest::genCnonce()
{
    return ABMUtils::aToHexString(EzText::genCharacterAndNumberPassword(16));
}

std::string EzDigest::genOpaque()
{
    return ABMUtils::aToHexString(EzText::genCharacterAndNumberPassword(16));
}

std::string EzDigest::getNonceCount(int count)
{
    char nc[16];
    sprintf(nc,"%08d",count);
    return std::string(nc,8);
};

std::string EzDigest::getSupportQop(std::string qop)
{
    std::vector<std::string> list = ABMUtils::split(qop,",");
    for (int i = 0 ; i < list.size() ; i++) {
        if(list.at(i) == "auth") return "auth";
        if(list.at(i) == "auth-int") return "auth-int";
    }
    return "";
}

std::string EzDigest::getMessageForWWWAuthenticate(bool stale)//WWW-Authenticate //服务器端
{
    std::map<std::string,std::string> map;
    map["realm"] = getRealm();
    map["qop"] = "auth,auth-int";
    map["nonce"] = genNonce(getETag(),getPrivateKey());
    map["opaque"] = genOpaque();
    map["algorithm"] = "MD5";
    if (stale) {
        map["stale"] = "true";
    }else{
        map["stale"] = "false";
    }
    return genDigestContent(map,"Digest ");
}
std::string EzDigest::getMessageForAuthenticationInfo(
                                                      std::string username,
                                                      std::string password,
                                                      std::string uri,
                                                      std::string body,
                                                      std::string realm,
                                                      std::string nc,
                                                      std::string algorithm,
                                                      std::string qop,
                                                      std::string cnonce
                                                      )//Authentication-Info //服务器端
{
    std::map<std::string,std::string> map;
    map["nextnonce"] = genNonce(getETag(),getPrivateKey());
    map["qop"] = qop;
    map["cnonce"] = cnonce;
    map["nc"] = nc;
    map["rspauth"] = genDigest(username,password,"",uri,body,realm,map["nextnonce"],nc,algorithm,cnonce,qop);
    return EzDigest::genDigestContent(map,"");
}
std::string EzDigest::getMessageForAuthenticate(
                                                std::string username,
                                                std::string password,
                                                std::string method,
                                                std::string uri,
                                                std::string body,
                                                std::string realm,
                                                std::string nonce,
                                                int nonceCount,
                                                std::string algorithm,
                                                std::string cnonce,
                                                std::string qop,
                                                std::string opaque
                                                )//Authenticate 客户端使用
{
    std::map<std::string,std::string> map;
    map["username"] = username;
    map["realm"] = realm;
    map["nonce"] = nonce;
    map["uri"] = uri;
    map["opaque"] = opaque;
    map["qop"] = qop;
    map["nc"] = EzDigest::getNonceCount(nonceCount);
    map["cnonce"] = cnonce;
    map["response"] = genDigest(username, password, method, uri, body, realm, nonce, EzDigest::getNonceCount(nonceCount), algorithm, cnonce, qop);
    return EzDigest::genDigestContent(map,"Digest ");
}
